#include "MetalMaterial.hpp"
#include "Microfacet.hpp"
#include "Params.hpp"

MetalMaterial::MetalMaterial(const Color &eta, const Color& k, const float roughness)
	: m_eta(eta), m_k(k), m_roughness(roughness) 
{
}

shared_ptr<BSDF> MetalMaterial::getBSDF(const HitInfo& hit) const
{
	auto bsdf = std::make_shared<BSDF>(hit, hit.normal);
	auto blinn = std::make_shared<Blinn>(1.0f / m_roughness);
	auto fresnel = std::make_shared<FresnelConductor>(m_eta, m_k);
	const Color reflectence(1.0f, 1.0f, 1.0f);
	bsdf->add(new Microfacet(&hit, reflectence, fresnel, blinn));
	return bsdf;
}

inline Color clampColor(const Color & c) {
	Color ret;
	ret.r = clamp(c.r, 0, 0.999f);
	ret.g = clamp(c.g, 0, 0.999f);
	ret.b = clamp(c.b, 0, 0.999f);
	return ret;
}

inline Color sqrtColor(const Color &c) {
	Color ret;
	ret.r = sqrtf(c.r);
	ret.g = sqrtf(c.g);
	ret.b = sqrtf(c.b);
	return ret;
}

static inline Color FresnelApproxEta(const Color &Fr) {
	Color reflectance = clampColor(Fr);
	return (Color(1.f, 1.f, 1.f) + sqrtColor(reflectance)) /
		(Color(1.f, 1.f, 1.f) - sqrtColor(reflectance));
}

static inline Color FresnelApproxK(const Color &Fr) {
	Color reflectance = clampColor(Fr);
	return 2.f * sqrtColor(reflectance / (Color(1.f, 1.f, 1.f) - reflectance));
}


ShinyMetalMaterial::ShinyMetalMaterial(const Color &ks, const Color& kr, const float roughness)
	: m_Ks(ks), m_Kr(kr), m_roughness(roughness)
{

}

shared_ptr<Material> ShinyMetalMaterial::create(const Params& params)
{
	Color ks = params.getColor("ks");
	Color kr = params.getColor("kr");
	float roughness = params.getFloat("roughness");
	return make_shared<ShinyMetalMaterial>(ks, kr, roughness);
}

shared_ptr<BSDF> ShinyMetalMaterial::getBSDF(const HitInfo& hit) const
{
	auto bsdf = std::make_shared<BSDF>(hit, hit.normal);
	auto blinn = std::make_shared<Blinn>(1.0f / m_roughness);
	const Color k(0.f, 0.f, 0.f);
	auto frMf = std::make_shared<FresnelConductor>(FresnelApproxEta(m_Ks), k);
	auto frSr = std::make_shared<FresnelConductor>(FresnelApproxEta(m_Kr), k);
	const Color reflectence(1.0f, 1.0f, 1.0f);
	bsdf->add(new Microfacet(&hit, reflectence, frMf, blinn));
	bsdf->add(new SpecularReflection(&hit, reflectence, frSr));
	return bsdf;
}
